Service介绍
Service是Android的四大组件之一,官方对它的描述是
A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
大概意思为,Service是一个应用程序组件,它是在一个应用程序不和用户进行交互时在后台执行的一个长时间运行的操作,或者为其他应用进程所使用的功能模块(这里应该是说Service可以被Activity或者其他应用进行bindService)。总之,我们对于Service的理解就是它是一个可以在后台运行不和用户进行交互的应用组件。
在刚开始接触Service,貌似觉得Service和Thread有些类似,后面随着深入了解后,才发现Service和Thread完全是两回事,那么它和Thread的区别在哪里?我觉得主要由以下几点:
-
Service作为Android组件,有它自己的声明周期,而Thread是操作系统中的概念,它是CPU分配时间片的最小单元。
-
Service是可以被其他组件进行操作的,比如Activity,而Thread不可以。
-
Service是运行于主线程上的,作为remote Service后,它是运行在独立的进程中的main线程上的,而Thread的运行是独立的,在new Thread后就于它所在的线程无关了。
Service的启动方式
startService
通过startService启动的Service会一直在后台运行,直到手动stopService或者stopSelf,这种方式启动的Service不和Activity组件进行交互,它经历的生命周期为onCreate->onStartCommand->onDestroy
bindService
通过bindService可以将Service和其他应用组件进行绑定,这时Service是以Server的形式为其提供服务的,绑定Service的组件可以调用Service提供的方法,这种方式同启动方式不同,绑定的service的生命周期通常只是在为组件提供服务时处于运行状态,并不时一直在后台运行,组件解除绑定后,service相应的就被销毁了。bindService的生命周期为 onCreate->onBind->onUnbind->onDestroy,需要注意的时多次bindService后只需要一次unbind即可.
远程服务
当在AndroidMainfest的Service标签中中指定了android:process时,代表Service将运行于独立的进程中。这时候如果通过bindService绑定服务可能就需要借助于AIDL了(Messenger也是可以的)。 否则组件和Sevice运行在相同的进程中,这时候只需要在服务端拓展Binder类后返回即可。
Service绑定和启动的转换
Service的绑定和启动是可以同时进行的,即可以同时通过startService和bindService对其进行调用,这时候按照先后顺序有不同的结果。
先绑定后再启动
这种方式启动后service,Service转为启动方式,也就是服务运行不受绑定组件的限制,会一直在后台运行,直到stopService或者stopSelf。
先启动后在绑定
这种情况下,Service先是以启动方式运行在后台,当组件对Service进行绑定后,仍然可以对Service继续请求操作,只是解除绑定后,Service依然会在后台继续运行,不受解绑的影响。
startService的源码分析
接下来,我们将通过阅读Service的源码来进一步了解Android中的Service是如果启动的。startService是从ContextImpl开始的,它是Context上下文的实现类,我们的Activity继承自ContextThemeWrapper,后者又继承自ContextWrapper,在ContextWrapper中有个Context的mBase成员,它实际上就是ContextImpl,因为Context只是一个抽象类。
1 | //Service的启动状态流程 |
startService开始调用startServiceCommon,在startServiceCommon中一开始先进行Intent的校验,在Android 4.4中对service的隐式启动做了安全警告,而在Android 5.1禁止通过隐式方式来启动service,接下来就是通过IPC Binder调用AMS的startService调用过程,这里需要注意传递的参数。
1 | //AMS端启动service |
在AMS的startService中进一步调用startServiceLocked并传递给其调用者pid和uid信息。
1 | //service的启动核心过程 |
首先通过调用者的信息获取到进程信息callerApp,随后通过retrieveServiceLocked获取到解析的Service在AndroidManifest的信息。在这个方法里会为Service创建ServiceRecord它在AMS端代表了当前启动的service,随后初始化了一些信息,比如将startRequested置为true表示请求进行启动service,第一次启动service时r.app是未绑定到ProcessRecord上的,通过getProcessRecordLocked获取service的进行信息,如果未指定android:process时,那么默认是在当前应用进程中启动。随后调用startServiceInnerLocked进一步启动service。
1 | ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, |
在startServiceInnerLocked中会调用bringUpServiceLocked进行一些启动的实质性工作,首先会检查ServiceRecord的app和app.thread,如果它们都不为null,说明service已经启动了,这时候走sendServiceArgsLocked的流程,后面我们看到它是如果工作的。
如果service设置了isolatedProcess,那么它会在一个单独的隔离进程中启动,否则通过AMS找到service的进程,如果它和应用进程是同一个进程,那么就直接调用realStartServiceLocked进行service的启动,否则service指定了启动的进程,这时候需要调用startProcessLocked通知zygote为其创建进程,并将其serviceRecord添加到mPendingServices,当进程创建完成后再从其中取出service进行启动流程,这个过程是异步进行的。
下面我们就分别看看这几种情况下的service是如何启动的:
- Service未启动的情况,这时候调用startProcessLocked进行启动
1 | //为应用或者服务创建进程 |
对于service进程不存在情况,需要通过zygote未service创建进程,这个过程同应用程序启动是类似的,Zygote未service创建完进程后,会在主线程中执行ActivityThread的main方法。这个主线程实际上就是ui线程。
1 | //为activity 和 service创建完进程后,会在主线程(ui线程)中执行ActivityThread的main方法 |
在main方法中会调用ActivityThread的attach方法,这里会通过binder调用AMS的attachApplication,实际上就是让AMS知道这个进程的存在。AMS中的attchApplication只是简单的调用attachApplicationLocked来进行进一步的启动。
1 | //在这里会启动等待新进程的activity或者service |
进程启动后,在AMS一端会去判断是否有需要启动的Activity或者service,之前我们了解到在启动service之前,AMS已经做好了准备,这里就去真正的启动service了。
frameworks/base/services/java/com/android/server/am/ActiveServices.java
1 | //进程创建后会通过这个方法来通知AMS |
在ActiveServices的attachApplicationLocked方法中会去从等待列表中取出要启动的service,然后通过realStartServiceLocked来进行真正的启动
1 | //这里是AMS中启动Service的最后一步,完成收尾工作 |
在realStartService中会通过IApplicationThread通知应用端服务的启动,在scheduleCreateService的IPC调用中,最终会触发service的onCreate回调。而在sendServiceArgsLocked会触发onStartCommand的回调,至此,service在ASM一端的启动过程就完成了。
- 当service的进程已经存在,那么直接调用realStartServiceLocked进行启动即可。
- 当service已经启动,这时候需要调用sendServiceArgsLocked来进一步完成启动,这里主要是回调onStartCommand
1 | private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, |
实际上这里是通过IApplicationThread回调应用端的scheduleServiceArgs方法,这是一个Binder的IPC调用。
1 | public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, |
可以看到scheduleServiceArgs最终会调用handleServiceArgs来进行onStartCommand的回调。